{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "private_outputs": true,
      "provenance": []
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "데이터 로드 및 준비"
      ],
      "metadata": {
        "id": "2Q-HXD0uEiDT"
      }
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w-JI0pXTEWvx"
      },
      "outputs": [],
      "source": [
        "# 1. 패키지 설치\n",
        "!pip install wfdb\n",
        "\n",
        "# 2. 데이터 다운로드 (한 번만)\n",
        "import wfdb\n",
        "wfdb.dl_database('qtdb', 'qtdb', records=['sel100'])\n",
        "\n",
        "\n",
        "# QTDB 샘플 불러오기 (필요에 따라 경로/이름만 바꾸면 됨)\n",
        "record = wfdb.rdrecord('qtdb/sel100')\n",
        "ecg_raw = record.p_signal[:, 0]\n",
        "annotation = wfdb.rdann('qtdb/sel100', 'q1c')\n",
        "samples = annotation.sample\n",
        "symbols = annotation.symbol\n",
        "\n",
        "\n",
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import pandas as pd"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "df = pd.DataFrame({'ecg_signal': ecg_raw})\n",
        "df.head(10)"
      ],
      "metadata": {
        "id": "Z8jHDsRSOnky"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "데이터 시각화"
      ],
      "metadata": {
        "id": "A8_yi-tcGyze"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "record = wfdb.rdrecord('qtdb/sel100')\n",
        "\n",
        "# 첫 번째 채널의 신호 (전체)\n",
        "ecg_raw = record.p_signal[:, 0]\n",
        "\n",
        "plt.figure(figsize=(15, 3))\n",
        "plt.plot(ecg_raw, color='tab:blue')\n",
        "plt.title(\"Original ECG Signal ('sel100')\")\n",
        "plt.xlabel(\"Sample Index\")\n",
        "plt.ylabel(\"Voltage (mV)\")\n",
        "plt.grid(True)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "5vrhNE-eGvVI"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "record = wfdb.rdrecord('qtdb/sel100')\n",
        "ecg_raw = record.p_signal[:, 0]\n",
        "\n",
        "# 앞쪽 2000포인트만 확대해서 보기\n",
        "view_len = 2000\n",
        "plt.figure(figsize=(15, 3))\n",
        "plt.plot(ecg_raw[:view_len], color='tab:blue')\n",
        "plt.title(f\"Original ECG Signal ('sel100') [First {view_len} points]\")\n",
        "plt.xlabel(\"Sample Index\")\n",
        "plt.ylabel(\"Voltage (mV)\")\n",
        "plt.grid(True)\n",
        "plt.tight_layout()\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "fVzuIJ1vG2dM"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "구간 기반 라벨링(Q wave:1, QRS wave: 2, ground:0)"
      ],
      "metadata": {
        "id": "HQ0H04LwEpo7"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "ecg_raw = (ecg_raw - np.mean(ecg_raw)) / np.std(ecg_raw)"
      ],
      "metadata": {
        "id": "AzlgsRrJDO__"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "label_map = {'p': 1, 'N': 2}  # Q파: 'p', QRS: 'N'\n",
        "y = np.zeros(len(ecg_raw), dtype=int)\n",
        "current_wave = None\n",
        "onset = None\n",
        "\n",
        "for idx, sym in enumerate(symbols):\n",
        "    sample_idx = samples[idx]\n",
        "    if sym == '(':\n",
        "        onset = sample_idx\n",
        "        if idx + 1 < len(symbols) and symbols[idx + 1] in label_map:\n",
        "            current_wave = symbols[idx + 1]\n",
        "    elif sym == ')':\n",
        "        offset = sample_idx\n",
        "        if current_wave is not None and onset is not None:\n",
        "            y[onset:offset+1] = label_map[current_wave]\n",
        "        onset = None\n",
        "        current_wave = None\n"
      ],
      "metadata": {
        "id": "f7G0LsfdE6np"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "라벨 구간만 자르기"
      ],
      "metadata": {
        "id": "NRegkDm_E-Mb"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# Load ECG signal\n",
        "record = wfdb.rdrecord('qtdb/sel100')\n",
        "ecg_raw = record.p_signal[:, 0]\n",
        "\n",
        "# Load annotation\n",
        "annotation = wfdb.rdann('qtdb/sel100', 'q1c')\n",
        "samples = annotation.sample\n",
        "symbols = annotation.symbol\n"
      ],
      "metadata": {
        "id": "KUFtnBO_G_Lv"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "idx_labeled = np.where(y != 0)[0]\n",
        "start_idx = np.min(idx_labeled)\n",
        "end_idx = np.max(idx_labeled)\n",
        "ecg_labeled = ecg_raw[start_idx:end_idx+1]\n",
        "y_labeled = y[start_idx:end_idx+1]\n"
      ],
      "metadata": {
        "id": "a03haM7_FA1n"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "plt.figure(figsize=(15, 3))\n",
        "plt.plot(ecg_labeled, label='ECG (standardized)', color='blue')\n",
        "plt.plot(y_labeled / 2 + np.min(ecg_labeled), label='Label (scaled)', color='red', alpha=0.6, linewidth=2)\n",
        "plt.title('Labeled ECG Region with Wave Segmentation')\n",
        "plt.xlabel('Sample Index (cropped)')\n",
        "plt.ylabel('Normalized Voltage (a.u.)')\n",
        "plt.legend()\n",
        "plt.grid(True)\n",
        "plt.show()\n"
      ],
      "metadata": {
        "id": "ZgAl_kVCHDLv"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# 샘플링 주파수(PhysioNet QTDB는 250Hz가 기본)\n",
        "fs = 250\n",
        "\n",
        "# 라벨(1,2)이 붙은 구간 인덱스 추출\n",
        "idx_labeled = np.where(y != 0)[0]\n",
        "start_idx = np.min(idx_labeled)\n",
        "end_idx = np.max(idx_labeled)\n",
        "length = end_idx - start_idx + 1\n",
        "\n",
        "print(f\"Label starts at: {start_idx} (sample index)\")\n",
        "print(f\"Label ends at:   {end_idx} (sample index)\")\n",
        "print(f\"Label region length: {length} samples\")\n",
        "print(f\"Label region duration: {length / fs:.2f} seconds\")\n",
        "print(f\"Label starts at: {start_idx / fs:.2f} sec, ends at: {end_idx / fs:.2f} sec\")"
      ],
      "metadata": {
        "id": "zYiIHlbMHIJV"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "zoom_start = 152000    # 관심있는 파형 구간 시작 인덱스 (원하는 곳으로 이동)\n",
        "zoom_len = 600         # 200포인트(샘플)만 보기\n",
        "\n",
        "plt.figure(figsize=(15, 4))\n",
        "plt.plot(np.arange(zoom_start, zoom_start + zoom_len), ecg_raw[zoom_start:zoom_start + zoom_len], label='ECG', color='blue')\n",
        "plt.plot(np.arange(zoom_start, zoom_start + zoom_len), y[zoom_start:zoom_start + zoom_len]/2 + np.min(ecg_raw),\n",
        "         label='Label (scaled)', color='red', alpha=0.7, linewidth=2)\n",
        "plt.title('ECG Signal and Wave Region Label (Detail View)')\n",
        "plt.xlabel('Sample Index')\n",
        "plt.ylabel('Voltage (mV)')\n",
        "plt.legend()\n",
        "plt.grid(True)\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "c_VQgBgWHz4b"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "print(y_labeled[:500])  # 앞쪽 1000개 값만 출력"
      ],
      "metadata": {
        "id": "kap5HqrqIiA3"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "윈도우 슬라이싱 & 특징 추출"
      ],
      "metadata": {
        "id": "1fibz40kFE2R"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "win_size = 4\n",
        "step = 2\n",
        "\n",
        "X_feat = []\n",
        "y_win = []\n",
        "\n",
        "for start in range(0, len(ecg_labeled) - win_size, step):\n",
        "    window = ecg_labeled[start:start+win_size]\n",
        "    center_label = y_labeled[start + win_size//2]\n",
        "    features = [\n",
        "        np.mean(window),\n",
        "        np.std(window),\n",
        "        np.max(window),\n",
        "        np.min(window),\n",
        "        np.ptp(window)\n",
        "    ]\n",
        "    X_feat.append(features)\n",
        "    y_win.append(center_label)\n",
        "\n",
        "X_feat = np.array(X_feat)\n",
        "y_win = np.array(y_win)"
      ],
      "metadata": {
        "id": "oWx7YROtFG4n"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "print(y_win[:100])  # window 중심 라벨도 비슷하게 반복되는지"
      ],
      "metadata": {
        "id": "utwN0MuMIvWn"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "데이터 분할"
      ],
      "metadata": {
        "id": "yLXuYf_rFIWp"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.model_selection import train_test_split\n",
        "\n",
        "X_train, X_test, y_train, y_test = train_test_split(\n",
        "    X_feat, y_win, test_size=0.3, random_state=42, stratify=y_win\n",
        ")"
      ],
      "metadata": {
        "id": "w1BmXLzjFJhR"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "KNN 데이터 분류/평가"
      ],
      "metadata": {
        "id": "5nh_0OErFLVD"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.neighbors import KNeighborsClassifier\n",
        "\n",
        "knn = KNeighborsClassifier(n_neighbors=3)\n",
        "knn.fit(X_train, y_train)\n",
        "y_pred = knn.predict(X_test)\n",
        "\n",
        "from sklearn.metrics import classification_report, confusion_matrix\n",
        "print(confusion_matrix(y_test, y_pred))\n",
        "print(classification_report(y_test, y_pred, zero_division=0))"
      ],
      "metadata": {
        "id": "x-kMUccpFLFK"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "실험2 파형 경계점 자동 검출"
      ],
      "metadata": {
        "id": "hq1eUkqWI_yu"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# y_labeled는 기존 구간 기반 라벨(0:배경, 1:Q파, 2:QRS파, ...)\n",
        "\n",
        "# Transition points: where label value changes\n",
        "transitions = []\n",
        "for i in range(1, len(y_labeled)):\n",
        "    prev, curr = y_labeled[i-1], y_labeled[i]\n",
        "    if prev != curr:\n",
        "        transitions.append((i, prev, curr))"
      ],
      "metadata": {
        "id": "fOf6HiByJDVh"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import matplotlib.pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "# 1) 라벨링이 붙은 부분만 추출\n",
        "idx_labeled = np.where(y != 0)[0]\n",
        "start_idx = np.min(idx_labeled)\n",
        "end_idx = np.max(idx_labeled)\n",
        "ecg_show = ecg_raw[start_idx:end_idx+1]\n",
        "y_show = y[start_idx:end_idx+1]\n",
        "\n",
        "# 2) Transition Point (Onset/Offset) 추출\n",
        "onsets_1 = [i for i in range(1, len(y_show)) if y_show[i-1]==0 and y_show[i]==1]\n",
        "offsets_1 = [i for i in range(1, len(y_show)) if y_show[i-1]==1 and y_show[i]==0]\n",
        "onsets_2 = [i for i in range(1, len(y_show)) if y_show[i-1]==0 and y_show[i]==2]\n",
        "offsets_2 = [i for i in range(1, len(y_show)) if y_show[i-1]==2 and y_show[i]==0]\n",
        "\n",
        "# 3) 시각화\n",
        "plt.figure(figsize=(15,4))\n",
        "plt.plot(ecg_show, label='ECG', color='blue')\n",
        "plt.plot(y_show/2 + np.min(ecg_show), label='Label (scaled)', color='gray', alpha=0.6, linewidth=2)\n",
        "plt.scatter(onsets_1, ecg_show[onsets_1], label='Q wave onset', color='red', marker='o')\n",
        "plt.scatter(offsets_1, ecg_show[offsets_1], label='Q wave offset', color='red', marker='x')\n",
        "plt.scatter(onsets_2, ecg_show[onsets_2], label='QRS onset', color='green', marker='o')\n",
        "plt.scatter(offsets_2, ecg_show[offsets_2], label='QRS offset', color='green', marker='x')\n",
        "plt.title('ECG with Detected Wave Boundaries (Onset/Offset)')\n",
        "plt.xlabel('Sample Index')\n",
        "plt.ylabel('Voltage (mV)')\n",
        "plt.legend()\n",
        "plt.grid(True)\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "MstiLwLaJIWn"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# 예: 보고 싶은 구간 지정 (start는 0 이상, end는 len(ecg_show) 이하로)\n",
        "zoom_start = 750    # 시작 인덱스 (직접 원하는 위치로 조정)\n",
        "zoom_len = 400      # 확대해서 보고 싶은 길이 (샘플 수)\n",
        "zoom_end = zoom_start + zoom_len\n",
        "\n",
        "# x축 (확대된 인덱스)\n",
        "x_axis = np.arange(zoom_start, zoom_end)\n",
        "\n",
        "# 확대된 신호, 라벨\n",
        "ecg_zoom = ecg_show[zoom_start:zoom_end]\n",
        "y_zoom = y_show[zoom_start:zoom_end]\n",
        "\n",
        "# 확대된 구간 내에서 transition point만 추출\n",
        "onsets_1_zoom = [i for i in range(1, zoom_len) if y_zoom[i-1]==0 and y_zoom[i]==1]\n",
        "offsets_1_zoom = [i for i in range(1, zoom_len) if y_zoom[i-1]==1 and y_zoom[i]==0]\n",
        "onsets_2_zoom = [i for i in range(1, zoom_len) if y_zoom[i-1]==0 and y_zoom[i]==2]\n",
        "offsets_2_zoom = [i for i in range(1, zoom_len) if y_zoom[i-1]==2 and y_zoom[i]==0]\n",
        "\n",
        "plt.figure(figsize=(15, 4))\n",
        "plt.plot(x_axis, ecg_zoom, label='ECG', color='blue')\n",
        "plt.plot(x_axis, y_zoom/2 + np.min(ecg_zoom), label='Label (scaled)', color='gray', alpha=0.6, linewidth=2)\n",
        "plt.scatter(x_axis[onsets_1_zoom], ecg_zoom[onsets_1_zoom], label='Q wave onset', color='red', marker='o')\n",
        "plt.scatter(x_axis[offsets_1_zoom], ecg_zoom[offsets_1_zoom], label='Q wave offset', color='red', marker='x')\n",
        "plt.scatter(x_axis[onsets_2_zoom], ecg_zoom[onsets_2_zoom], label='QRS onset', color='green', marker='o')\n",
        "plt.scatter(x_axis[offsets_2_zoom], ecg_zoom[offsets_2_zoom], label='QRS offset', color='green', marker='x')\n",
        "plt.title('Zoomed ECG with Detected Wave Boundaries')\n",
        "plt.xlabel('Sample Index (zoomed)')\n",
        "plt.ylabel('Voltage (mV)')\n",
        "plt.legend()\n",
        "plt.grid(True)\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "9ytaggRbJQGD"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "실험2 훈련 및 테스트"
      ],
      "metadata": {
        "id": "JbCCyh8CJ9ON"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from scipy.stats import mode\n",
        "\n",
        "win_size = 30\n",
        "step = 10\n",
        "\n",
        "X_feat = []\n",
        "y_win = []\n",
        "\n",
        "for start in range(0, len(ecg_labeled) - win_size, step):\n",
        "    window = ecg_labeled[start:start + win_size]\n",
        "    window_labels = y_labeled[start:start + win_size]\n",
        "    # mode() 함수의 결과를 항상 .mode[0]로 가져오기\n",
        "    label = mode(window_labels, keepdims=True).mode[0]\n",
        "    features = [\n",
        "        np.mean(window),\n",
        "        np.std(window),\n",
        "        np.max(window),\n",
        "        np.min(window),\n",
        "        np.ptp(window)\n",
        "    ]\n",
        "    X_feat.append(features)\n",
        "    y_win.append(label)\n",
        "\n",
        "X_feat = np.array(X_feat)\n",
        "y_win = np.array(y_win)"
      ],
      "metadata": {
        "id": "e2PVka1qKClh"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# 2. Train/test split\n",
        "X_train, X_test, y_train, y_test = train_test_split(\n",
        "    X_feat, y_win, test_size=0.3, random_state=42, stratify=y_win\n",
        ")\n"
      ],
      "metadata": {
        "id": "KfVXTSBLKH_h"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "모델 평가"
      ],
      "metadata": {
        "id": "j4FB_qXeKKDY"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "#KNN 모델 훈련/예측/평가\n",
        "knn = KNeighborsClassifier(n_neighbors=3)\n",
        "knn.fit(X_train, y_train)\n",
        "y_pred = knn.predict(X_test)\n",
        "\n",
        "print(confusion_matrix(y_test, y_pred))\n",
        "print(classification_report(y_test, y_pred, zero_division=0))"
      ],
      "metadata": {
        "id": "-3C0KgoEKL0N"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.ensemble import RandomForestClassifier\n",
        "\n",
        "rf = RandomForestClassifier(n_estimators=100, random_state=42)\n",
        "rf.fit(X_train, y_train)\n",
        "y_pred = rf.predict(X_test)\n",
        "\n",
        "from sklearn.metrics import classification_report, confusion_matrix\n",
        "print(confusion_matrix(y_test, y_pred))\n",
        "print(classification_report(y_test, y_pred, zero_division=0))"
      ],
      "metadata": {
        "id": "3OjbvbwbaLbs"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "# 정답 경계점\n",
        "gt_onset = [i for i in range(1, len(y_test)) if y_test[i-1]==0 and y_test[i]==1]\n",
        "# 예측 경계점\n",
        "pred_onset = [i for i in range(1, len(y_pred)) if y_pred[i-1]==0 and y_pred[i]==1]\n",
        "\n",
        "print(\"GT onsets:\", gt_onset)\n",
        "print(\"Pred onsets:\", pred_onset)\n",
        "\n",
        "# 예를 들어 10샘플 이내로 맞췄으면 TP로 보는 식의 평가도 가능\n",
        "def count_matched(gt, pred, tol=10):\n",
        "    matched = 0\n",
        "    for g in gt:\n",
        "        if any(abs(p-g) <= tol for p in pred):\n",
        "            matched += 1\n",
        "    return matched, len(gt)\n",
        "\n",
        "matched, total = count_matched(gt_onset, pred_onset)\n",
        "print(f\"Q-wave onset detection rate (within 10 samples): {matched}/{total} = {matched/total:.2f}\")\n"
      ],
      "metadata": {
        "id": "etxfdo6IKOd5"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "정답 vs 예측 시각화"
      ],
      "metadata": {
        "id": "pvM8Q4dGFQla"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.metrics import ConfusionMatrixDisplay\n",
        "\n",
        "cm = confusion_matrix(y_test, y_pred)\n",
        "disp = ConfusionMatrixDisplay(confusion_matrix=cm)\n",
        "disp.plot(cmap=plt.cm.Blues, values_format='d')\n",
        "plt.title(\"Confusion Matrix (KNN Test Set)\")\n",
        "plt.show()"
      ],
      "metadata": {
        "id": "EH6O1Io3GhLp"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "경계점 검출/정확도 평가"
      ],
      "metadata": {
        "id": "b9tv1JRkFUhO"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "# 예: Q파 시작점(0→1) 검출 성능\n",
        "gt_onset = [i for i in range(1, len(y_test)) if y_test[i-1]==0 and y_test[i]==1]\n",
        "pred_onset = [i for i in range(1, len(y_pred)) if y_pred[i-1]==0 and y_pred[i]==1]\n",
        "\n",
        "def count_matched(gt, pred, tol=10):\n",
        "    matched = 0\n",
        "    for g in gt:\n",
        "        if any(abs(p-g) <= tol for p in pred):\n",
        "            matched += 1\n",
        "    return matched, len(gt)\n",
        "\n",
        "matched, total = count_matched(gt_onset, pred_onset)\n",
        "print(f\"Q-wave onset detection rate (within 10 samples): {matched}/{total} = {matched/total:.2f}\")\n"
      ],
      "metadata": {
        "id": "7lFdYm83FWkt"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "n_show = 150\n",
        "plt.figure(figsize=(15, 3))\n",
        "plt.plot(y_test[:n_show], label='True label', linewidth=2, color='red')\n",
        "plt.plot(y_pred[:n_show], label='Predicted', linewidth=2, color='blue', alpha=0.6)\n",
        "plt.legend()\n",
        "plt.title('True vs Predicted Label Sequence (First 150 Windows)')\n",
        "plt.xlabel('Window Index')\n",
        "plt.ylabel('Class Label')\n",
        "plt.grid(True)\n",
        "plt.show()\n"
      ],
      "metadata": {
        "id": "EYAgvRQVKV2_"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}